package cn.edu.nwpu.warehousemanagement.service.impl;

import cn.edu.nwpu.warehousemanagement.constantEnum.FreightStatus;
import cn.edu.nwpu.warehousemanagement.constantEnum.OperationType;
import cn.edu.nwpu.warehousemanagement.dao.*;
import cn.edu.nwpu.warehousemanagement.pojo.*;
import cn.edu.nwpu.warehousemanagement.pojo.dto.OutboundDTO;
import cn.edu.nwpu.warehousemanagement.service.OutboundService;
import cn.edu.nwpu.warehousemanagement.pojo.Result;
import cn.edu.nwpu.warehousemanagement.util.ThreadLocalUtil;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 出库服务
 * @author LSR
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class OutboundServiceImpl implements OutboundService {

    // 批量处理大小
    private static final int BATCH_SIZE = 1000;

    private final FreightRepository freightRepository;
    private final ProductRepository productRepository;
    private final InventoryRecordRepository inventoryRecordRepository;
    private final ShelfRepository shelfRepository;

    @Override
    @Transactional
    public Result<?> outbound(OutboundDTO outboundDTO) {
        if (outboundDTO == null || outboundDTO.getFreightIds() == null || outboundDTO.getFreightIds().isEmpty()) {
            return Result.error("出库数据不能为空");
        }

        log.info("出库操作开始，操作员: {}, 货物数量: {}",
                ThreadLocalUtil.getLoginUserId(),
                outboundDTO.getFreightIds().size());

        // 1. 批量加载货物并校验
        List<Long> freightIds = outboundDTO.getFreightIds();
        Map<Long, Freight> freightMap = loadAndValidateFreights(freightIds);
        if (freightMap == null) {
            return Result.error("货物校验失败");
        }

        // 2. 按商品分组统计出库数量
        Map<Product, Long> quantityMap = calculateProductQuantities(freightMap.values());
        validateStockSufficiency(quantityMap);

        // 3. 更新货物状态为已出库
        updateFreightStatus(freightIds);

        // 4. 创建出库记录
        createInventoryRecord(outboundDTO, new ArrayList<>(freightMap.values()));

        // 5. 更新商品库存
        updateProductQuantities(quantityMap);

//        log.info("出库成功，批次: {}, 订单: {}", batchId, outboundDTO.getOrderId());

        return Result.success("出库成功");
    }

    // 批量加载货物并校验
    private Map<Long, Freight> loadAndValidateFreights(List<Long> freightIds) {
        Map<Long, Freight> freightMap = new ConcurrentHashMap<>();
        List<Freight> validFreights = new ArrayList<>();

        // 分批加载货物
        int total = freightIds.size();
        for (int i = 0; i < total; i += BATCH_SIZE) {
            int end = Math.min(i + BATCH_SIZE, total);
            List<Long> batchIds = freightIds.subList(i, end);

            List<Freight> batch = freightRepository.findByIdInAndDeletedFalse(batchIds);
            if (batch.size() != batchIds.size()) {
                log.error("部分货物不存在或已被删除，请求ID: {}, 实际找到: {}", batchIds, batch);
                return null;
            }

            // 校验货物状态
            for (Freight freight : batch) {
                if (freight.getStatus() != FreightStatus.IN_STOCK) {
                    log.error("货物状态异常，ID: {}, 状态: {}", freight.getId(), freight.getStatus());
                    return null;
                }
                freightMap.put(freight.getId(), freight);
            }
        }
        return freightMap;
    }

    // 按商品分组统计出库数量
    private Map<Product, Long> calculateProductQuantities(Collection<Freight> freights) {
        Map<Product, Long> quantityMap = new ConcurrentHashMap<>();
        freights.forEach(freight -> {
            quantityMap.merge(freight.getProduct(), 1L, Long::sum);
        });
        return quantityMap;
    }

    //出库前检测，确保库存量不会为负数
    private void validateStockSufficiency(Map<Product, Long> quantityMap) {
        quantityMap.forEach((product, reduceQty) -> {
            if (product.getQuantity() < reduceQty) {
                throw new RuntimeException("商品[" + product.getName() + "]库存不足");
            }
        });
    }

    // 批量更新货物状态
    private void updateFreightStatus(List<Long> freightIds) {
        int total = freightIds.size();
        for (int i = 0; i < total; i += BATCH_SIZE) {
            int end = Math.min(i + BATCH_SIZE, total);
            List<Long> batchIds = freightIds.subList(i, end);

            freightRepository.batchUpdateStatus(
                    batchIds,
                    FreightStatus.SHIPPED
            );
            freightRepository.flush();
        }
    }

    // 创建出库记录
    private void createInventoryRecord(OutboundDTO outboundDTO, List<Freight> freights) {
        // 生成批次ID (使用时间戳+随机数避免冲突)
//        String batchId = "OUT-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmmss")) + "-" + new Random().nextInt(1000);
//        String batchId = generateBatchId();
        String timePart = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmmss"));
        String randomPart = String.format("%03d", new Random().nextInt(1000));
        String batchId = "O-" + timePart + randomPart; // 总长度固定17位

        InventoryRecord record = new InventoryRecord(
                OperationType.OUTBOUND,
                ThreadLocalUtil.getLoginUserId(), // 获取当前操作员ID
                LocalDateTime.now(),
                batchId
        );

        record.setFreights(freights);
        record.setRelatedOrderId(outboundDTO.getOrderId());
        record.setDeleted(false);

        inventoryRecordRepository.save(record);
    }

    // 更新商品库存
    private void updateProductQuantities(Map<Product, Long> quantityMap) {
        quantityMap.forEach((product, delta) -> {
            try {
                // 使用负增量减少库存
                productRepository.updateQuantity(product.getId(), -delta);
            } catch (Exception e) {
                log.error("更新库存失败: productId={}, delta={}", product.getId(), -delta, e);
            }
        });
    }

//    // 生成批次ID
//    private String generateBatchId() {
//        return "OUT-" + System.currentTimeMillis() % 1000000000 + "-" + (int)(Math.random() * 10000);
//    }
}
